import {
  Injectable,
  Inject,
  InternalServerErrorException,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';

import { LoginUsersModel } from '@server/models';

import Config from '@server/config';

class TokenParsed {
  id?: number;
  username?: string;
  password?: string;
}

@Injectable()
export class AuthService {
  constructor(
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
    @Inject('LOGIN_USERS_MODEL')
    private readonly loginUserModel: typeof LoginUsersModel,
  ) {}

  public signIn(id: number, username: string, password: string): string {
    const prefix: string = 'Bearer';
    const token: string = this.jwtService.sign(
      {
        id,
        username,
        password,
      },
      {
        expiresIn: this.configService.get('jwt.expiresIn'),
      },
    );
    const { exp, iat } = this.jwtService.decode(token) as any;
    const authToken: string = `${prefix} ${token}`;

    this.loginUserModel.create({
      iat,
      token,
      exp: (exp - 60 * 60) * 1000,
      userId: id,
    });

    return authToken;
  }

  public async signOut(token: string): Promise<void> {
    const { iat, id: userId } = this.jwtService.decode(
      token.replace(/^Bearer/, '').trim(),
    ) as any;
    await this.loginUserModel.findOneAndDelete({ iat, userId });
  }

  public parse(token: string): TokenParsed {
    const authToken: string = token.replace(/^Bearer/, '').trim();

    try {
      return this.jwtService.verify(authToken);
    } catch (e) {
      throw new InternalServerErrorException();
    }
  }

  async validateUser(payload: any): Promise<any> {
    const tokenInfo: any = await this.loginUserModel.findOne({
      lat: payload.lat,
      userId: payload.id,
    });

    if (!tokenInfo || tokenInfo.exp < Date.now()) {
      return null;
    }

    if (payload.lat * 1000 < Date.now()) {
      return null;
    }
    return payload;
  }
}
